<?php
/**
 * JoiGallery - image gallery component for Joostina
 *
 * Image Classes
 *
 * @version 1.0 alpha 12
 * @package JoiGallery
 * @filename image.class.php
 * @author JoostinaTeam
 * @copyright (C) 2008-2009 Joostina Team
 * @license see license.txt
 *
 **/

defined('_VALID_MOS') or die();

class joiGalleryUploader {
	
  var $type = null;
  var $allow_img_formats = null;
  var $upload_file = null;
  var $tempdir = null;
  var $error = _C_JOIGALLERY_MSG_UPLOAD_ERR_UNKNOWN;


  function joiGalleryUploader($type = 'upload_single', $allow_img_formats = null ) {

    $this->type = $type;
    if(!$allow_img_formats) //Поддерживаемые форматы:
         $this->allow_img_formats = array('image/pjpeg', 
                                          'image/jpeg',
                                          'image/jpg',
                                          'image/png',
                                          'image/x-png',
                                          'image/gif',
                                          'image/bmp');
    else $this->allow_img_formats = $allow_img_formats;

  }

  //TODO: Сделать загрузку изображение и zip-архив через одну форму
  function process_upload($dir = '') {

    switch($this->type) {
            case 'upload_single':
            default:
                    return $this->upload_single();
                    break;

            case 'upload_zip_archive':
                    return $this->upload_zip_archive();
                    break;

            case 'upload_directory':
                    return $this->upload_directory($dir);
                    break;
    }

  }

	
  function upload_single() {

    if ($_FILES['photo']['error'] > 0) {
      switch ($_FILES['photo']['error']) {

          case UPLOAD_ERR_INI_SIZE:
              $error = _C_JOIGALLERY_MSG_UPLOAD_ERR_INI_SIZE;
              break;

          case UPLOAD_ERR_FORM_SIZE:
              $error = _C_JOIGALLERY_MSG_UPLOAD_ERR_FORM_SIZE;
              break;

          case UPLOAD_ERR_PARTIAL:
              $error = _C_JOIGALLERY_MSG_UPLOAD_ERR_PARTIAL;
              break;

          case UPLOAD_ERR_NO_FILE:
              $error = _C_JOIGALLERY_MSG_UPLOAD_ERR_NO_FILE;
              break;

          case UPLOAD_ERR_NO_TMP_DIR:
              $error = _C_JOIGALLERY_MSG_UPLOAD_ERR_NO_TMP_DIR;
              break;

          case UPLOAD_ERR_CANT_WRITE:
              $error = _C_JOIGALLERY_MSG_UPLOAD_ERR_CANT_WRITE;
              break;

          case UPLOAD_ERR_EXTENSION:
              $error= _C_JOIGALLERY_MSG_UPLOAD_ERR_EXTENSION;
              break;

          default:
              $error = _C_JOIGALLERY_MSG_UPLOAD_ERR_UNKNOWN;
              break;
      }

      $this->error = sprintf (_C_JOIGALLERY_MSG_UPLOAD_ERR_FOR_FILE, $_FILES['photo']['name'], $error);
      return false;

    }

    if (!in_array($_FILES['photo']['type'], $this->allow_img_formats)) {

        $this->error = sprintf (_C_JOIGALLERY_MSG_UPLOAD_ERR_PHOTO_TYPE, $_FILES['photo']['type']);
       
        return false;
    }

    return true;
  }
	
	function upload_zip_archive(){
		
            $mainframe = &mosMainFrame::getInstance();
		
            if ($_FILES['zip']['error'] > 0) {
                switch ($_FILES['zip']['error']) {
                    case UPLOAD_ERR_INI_SIZE:
                        $err_msg = _C_JOIGALLERY_MSG_UPLOAD_ERR_INI_SIZE;
                        break;
                    case UPLOAD_ERR_FORM_SIZE:
                        $err_msg = _C_JOIGALLERY_MSG_UPLOAD_ERR_FORM_SIZE;
                        break;
                    case UPLOAD_ERR_PARTIAL:
                        $err_msg = _C_JOIGALLERY_MSG_UPLOAD_ERR_PARTIAL;
                        break;
                    case UPLOAD_ERR_NO_FILE:
                        $err_msg = _C_JOIGALLERY_MSG_UPLOAD_ERR_NO_FILE;
                        break;
                    case UPLOAD_ERR_NO_TMP_DIR:
                        $err_msg = _C_JOIGALLERY_MSG_UPLOAD_ERR_NO_TMP_DIR;
                        break;
                    case UPLOAD_ERR_CANT_WRITE:
                        $err_msg = _C_JOIGALLERY_MSG_UPLOAD_ERR_CANT_WRITE;
                        break;
                    case UPLOAD_ERR_EXTENSION:
                        $err_msg = _C_JOIGALLERY_MSG_UPLOAD_ERR_EXTENSION;
                        break;
                    default:
                        $err_msg = _C_JOIGALLERY_MSG_UPLOAD_ERR_UNKNOWN;
                        break;
                }

                $this->error =  sprintf (_C_JOIGALLERY_MSG_UPLOAD_ERR_FOR_FILE, $_FILES['zip']['name'], $err_msg);
                return false;                
            }
	        
	        if(!$this->check_tmpdir()){
	        	return false;	        	
	        }
	        
            $iswin = (substr(PHP_OS, 0, 3) == 'WIN');

            if (eregi('.zip$', $_FILES['zip']['name'])) {

                require_once (JPATH_BASE.'/administrator/includes/pcl/pclzip.lib.php');
                require_once (JPATH_BASE.'/administrator/includes/pcl/pclerror.lib.php');

                $zipfile = new PclZip($_FILES['zip']['tmp_name']);
                if ($iswin) {
                    define('OS_WINDOWS', 1);
                }
                else {
                    define('OS_WINDOWS', 0);
                }

                $ret = $zipfile->extract(PCLZIP_OPT_PATH, $this->tmpdir);
                if ($ret == 0) {
                    $this->error = _C_JOIGALLERY_MSG_UPLOAD_ERR_UNRECOVERABLE . $zipfile->errorName(true);
                    return false;
                }
            }
            else {
                require_once (JPATH_BASE.'/includes/Archive/Tar.php');
                $archive = new Archive_Tar($_FILES['zip']['tmp_name']);
                $archive->setErrorHandling(PEAR_ERROR_PRINT);

                if (!$archive->extractModify($this->tmpdir, '')) {
                    $this->error = _C_JOIGALLERY_MSG_UPLOAD_ERR_EXTRACT;
                    return false;
                }
            }
            
            return true;
	}
	
	function upload_directory($dir){
		
		$mainframe = &mosMainFrame::getInstance();
		
		if (!$dir || $dir == JPATH_BASE) {
                    $this->error = _C_JOIGALLERY_MSG_UPLOAD_ERR_DIR_NOT_DEFINED;
                    return false;
		}
		
		if (!is_dir($dir)) {
                    $this->error = sprintf (_C_JOIGALLERY_MSG_UPLOAD_ERR_DIR_NOT_EXISTS, $dir);
                    return false;
  		}
  		
        if(!$this->check_tmpdir()) return false;
        
        return true;

	}
	
	function check_tmpdir() {
          
		$mainframe = &mosMainFrame::getInstance();
		$time = time();
		
		$tmpdir = '';
            if (!mkdir(JPATH_BASE.'/media/'.$time)) {
                if (!mkdir('/tmp/'.$time)) {
                     $this->error = _C_JOIGALLERY_MSG_UPLOAD_ERR_TMP_DIR;
                     return false;
                }
                else $tmpdir = '/tmp/'.$time;
                
            }
            else $tmpdir = JPATH_BASE.'/media/'.$time;            
			
            return $this->tmpdir = $tmpdir;
		
	}
	
}

class joiGalleryImage {

	var $temp_path = null;
	var $target_dir_original = null;
	var $target_dir_middle = null;
	var $target_dir_thumbs = null;
	var $file_prefix = 'img_';
	var $filename = null;
	var $error = _C_JOIGALLERY_MSG_UPLOAD_ERR_UNKNOWN;

	function joiGalleryImage($file_options = array()) {
        foreach($file_options as $key=>$v) {
                $this->$key = $v;
        }
	}
	
	function upload() {
	
		//$this->file_prefix = ($this->file_prefix) ? $this->file_prefix.'_' : '';
		
		//Если директория для категории еще не создана - пытаемся создать её
		if(!self::check_dir($this->target_dir_original)){
              $this->error = _C_JOIGALLERY_MSG_UPLOAD_ERR_ORIG_DIR;
              return false;
		}
		
		//Перемещаем оригинал в целевую директорию
        if (!copy($this->temp_path, $this->target_dir_original . '/' . $this->file_prefix . "_" . $this->filename)) {
              $this->error = sprintf (_C_JOIGALLERY_MSG_UPLOAD_ERR_COPY_FAIL, $this->filename);
              return false;
        }

        //Копируем заглушку noimage в директорию оригиналов, если ее там нет
        if (!file_exists($this->target_dir_original.'/noimage.jpg')) {
              copy ($this->noimage_file, $this->target_dir_original.'/noimage.jpg');
        }

        return true;
    }
	
        
    //Создание средних эскизов
	function create_middle($resize_options, $suffix = true) {
	
		$suffix = ($suffix) ? '_' : '';
				
        if(!self::check_dir($this->target_dir_middle)){
           $this->error = _C_JOIGALLERY_MSG_UPLOAD_ERR_MIDDLE_DIR;
           return false;
        }

        //Если размер оригинала меньше размера среднего эскиза, указанного в настройках - не увеличивать.
        if ($resize_options[upscale] == 0) {
            $tmp_size = getimagesize($this->temp_path);
            if($tmp_size[0] < $resize_options[width] || $tmp_size[1] < $resize_options[height]) {
               $resize_options[width]  = $tmp_size[0];
               $resize_options[height] = $tmp_size[1];
            }
        }

        if(!Thumbnail::output($this->temp_path, $this->target_dir_middle . '/' . $this->file_prefix . $suffix . $this->filename, $resize_options)){
           $this->error = _C_JOIGALLERY_MSG_UPLOAD_ERR_MIDDLE_THUMB;
           return false;
        }

        //Создаем заглушку noimage в директории средних эскизов, если ее там нет
        if (!file_exists($this->target_dir_middle.'/noimage.jpg')) {
            Thumbnail::output($this->noimage_file, $this->target_dir_middle.'/noimage.jpg', $resize_options);
        }

        return true;
    }

    //Создание мини-эскизов
	function create_thumb($resize_options, $suffix = true) {
	
		$suffix = ($suffix) ? '_' : '';
		
        if(!self::check_dir($this->target_dir_thumbs)){
           $this->error = _C_JOIGALLERY_MSG_UPLOAD_ERR_THUMB_DIR;
           return false;
        }

        if(!Thumbnail::output($this->temp_path, $this->target_dir_thumbs . '/' . $this->file_prefix . $suffix . $this->filename, $resize_options)){
           $this->error = _C_JOIGALLERY_MSG_UPLOAD_ERR_THUMB;
           return false;
        }

        //Создаем заглушку noimage в директории эскизов, если ее там нет
        if (!file_exists($this->target_dir_thumbs.'/noimage.jpg')) {
            Thumbnail::output($this->noimage_file, $this->target_dir_thumbs.'/noimage.jpg', $resize_options);
        }

        return true;
	}
	
	
	function check_dir($dir){

        if (!is_dir($dir)) {
            if (!mkdir($dir)) {
                return false;
            }
            mosChmod($dir, 0755);
            return true;
        }

        return true;
	}

	function isFileIsset($file) {

        $file_name = JPATH_BASE.'/'.$file;
        if(file_exists($file_name)) return true;
        else return false;
		
	}


	function delFile(&$file) {
		
        $file_name = $file->directory.'/'.$file->name;
		
        if(self::isFileIsset($file_name)){
           unlink(JPATH_BASE.'/'.$file_name);
           return true;
        }

        return false;
	}


}

class water{

        var $fileHandle = null;
        var $newPictureName = null;
        var $pictureInfo = null;
        var $pictureName = null;
        var $prefix = null;
        var $stampInfo = null;
        var $stampName = null;
        var $stampXpos = null;
        var $stampYpos = null;

        function __construct($picture, $stamp, $position = 'top_left') {

                $this->pictureName = $picture; 
                $this->stampName = $stamp;
                $this->stampPosition = $position;                 
        }


        public static function GDversion() {

          // GD 2.0.28 or newer is recommended version to use
          var_dump(gd_info()); // dump information about your GD version

          return true;
        }



        private function openImage($fileName, $type) {

                // open picture with correct image function. Add more types if needed.
                // GIF: http://php.net/manual/en/function.imagecreatefromgif.php
                // BMP: http://www.php.net/manual/en/function.imagecreatefromwbmp.php
                // JPG/JPEG: http://php.net/manual/en/function.imagecreatefromjpeg.php
                // PNG: http://php.net/manual/en/function.imagecreatefrompng.php
                switch ($type){

                case 1: // GIF
                        $this->fileHandle = imagecreatefromgif($fileName);
                        break;// case 1

                case 2: // JPG/JPEG
                        $this->fileHandle = imagecreatefromjpeg($fileName);
                        break;// case 2

                case 3: // PNG
                        $this->fileHandle = imagecreatefrompng($fileName);
                        break;// case 3

                case 6: // BMP
                        $this->fileHandle = imagecreatefromwbmp($fileName);
                        break;// case 4

                default:
                        die(_C_JOIGALLERY_MSG_UPLOAD_ERR_UNSUPPORTED_FILE_TYPE . $fileName);
                }

        return $this->fileHandle;
        }



        public function stampPicture(){

                // Получаем информацию о загружаемом изображении: ширина, высота, расширение                
                // http://php.net/manual/en/function.getimagesize.php
                $this->pictureInfo = getimagesize($this->pictureName) or die(_C_JOIGALLERY_MSG_UPLOAD_ERR_GETTING_FILE_INFO);
                $pic_width = $this->pictureInfo[0]; //ширина
                $pic_height = $this->pictureInfo[1]; //высота

                //Информация о ватермарке
                $this->stampInfo = getimagesize($this->stampName) or die(_C_JOIGALLERY_MSG_UPLOAD_ERR_GETTING_WATER_INFO);
                $stamp_width = $this->stampInfo[0]; //ширина
                $stamp_height = $this->stampInfo[1]; //высота

                // open images with class method openImage()
                $this->pictureFile = $this->openImage($this->pictureName, $this->pictureInfo[2]);
                $this->stampFile = $this->openImage($this->stampName, $this->stampInfo[2]);

                // position the stamp              
                $this->get_cords($pic_width, $pic_height, $stamp_width, $stamp_height);

                imagealphablending($this->pictureFile, TRUE);

                $finalWaterMarkImage  = $this->stampFile;
                $finalWaterMarkWidth  = imagesx($finalWaterMarkImage);
                $finalWaterMarkHeight = imagesy($finalWaterMarkImage);

                imagecopy($this->pictureFile,
                          $finalWaterMarkImage,
                          $this->stampXpos,
                          $this->stampYpos,
                          0,
                          0,
                          $finalWaterMarkWidth,
                          $finalWaterMarkHeight
                );


                // set a new name for the stamped picture and keep the original picture intact
                $this->newPictureName = $this->pictureName;
				
				//$quality = (int) $options['quality'];  
				//$quality = 20; 

                switch ($this->pictureInfo[2]){

                case 1: // GIF
                        imagegif($this->pictureFile, $this->newPictureName);
                        break;// case 1

                case 2:// JPG/JPEG
                        imagejpeg($this->pictureFile, $this->newPictureName/* , $quality */);
                        break;// case 2

                case 3: // PNG
                        imagepng($this->pictureFile, $this->newPictureName);
                        break;// case 3

                case 6: // BMP
                        imagewbmp($this->pictureFile, $this->newPictureName);
                        break;// case 4

                default:
                        die(_C_JOIGALLERY_MSG_UPLOAD_ERR_UNSUPPORTED_FILE_TYPE . $fileName);

            }

        return true;
        }
        
        function get_cords($pic_width, $pic_height, $stamp_width, $stamp_height){
            
            switch($this->stampPosition){
                
                case 'top_left':
                default:
                    $this->stampXpos = 0;
                    $this->stampYpos = 0;
                return;
                
                case 'top_right':
                    $this->stampXpos = round(($pic_width  - $stamp_width), 0);
                    $this->stampYpos = 0;
                return;
                
                case 'top_center':
                    $this->stampXpos = round(($pic_width  - $stamp_width) / 2, 0);
                    $this->stampYpos = 0;
                return;
                
                case 'bottom_left':
                    $this->stampXpos = 0;
                    $this->stampYpos = $pic_height - $stamp_height;
                return;
                
                case 'bottom_right':
                    $this->stampXpos = round(($pic_width  - $stamp_width), 0);
                    $this->stampYpos = $pic_height - $stamp_height;
                return;
                
                case 'bottom_center':
                    $this->stampXpos = round(($pic_width  - $stamp_width) / 2, 0);
                    $this->stampYpos = $pic_height - $stamp_height;
                return;
                
                case 'middle':
                    $this->stampXpos = round(($pic_width  - $stamp_width) / 2, 0);
                    $this->stampYpos = round(($pic_height - $stamp_height) / 2, 0);
                return;
                
            }
          
        }

        function destruct(){
                unset($fileHandle, $newPictureName, $pictureInfo, $pictureName, $prefix, $stampInfo, $stampName, $stampXpos, $stampYpos);
        }



}


if (! class_exists('Thumbnail')) {

/**
 * Maximal scaling
 */
defined('THUMBNAIL_METHOD_SCALE_MAX') or define('THUMBNAIL_METHOD_SCALE_MAX', 0);

/**
 * Minimal scaling
 */
defined('THUMBNAIL_METHOD_SCALE_MIN') or define('THUMBNAIL_METHOD_SCALE_MIN', 1);

/**
 * Cropping of fragment
 */
defined('THUMBNAIL_METHOD_CROP')    or define('THUMBNAIL_METHOD_CROP', 2);

/**
 * Align constants
 */
defined('THUMBNAIL_ALIGN_CENTER')   or define('THUMBNAIL_ALIGN_CENTER', 0);
defined('THUMBNAIL_ALIGN_LEFT')     or define('THUMBNAIL_ALIGN_LEFT',   -1);
defined('THUMBNAIL_ALIGN_RIGHT')    or define('THUMBNAIL_ALIGN_RIGHT',  +1);
defined('THUMBNAIL_ALIGN_TOP')      or define('THUMBNAIL_ALIGN_TOP',	-1);
defined('THUMBNAIL_ALIGN_BOTTOM')   or define('THUMBNAIL_ALIGN_BOTTOM', +1);

// }}}
// {{{

class Thumbnail
{

	// {{{

	/**
	 * Create a GD image resource from given input.
	 *
	 * This method tried to detect what the input, if it is a file the
	 * createImageFromFile will be called, otherwise createImageFromString().
	 *
	 * @param  mixed $input The input for creating an image resource. The value
	 *					  may a string of filename, string of image data or
	 *					  GD image resource.
	 *
	 * @return resource	 An GD image resource on success or false
	 * @access public
	 * @static
	 * @see	Thumbnail::imageCreateFromFile(), Thumbnail::imageCreateFromString()
	 */
	function imageCreate($input) {

          if      ( is_file($input) )   return Thumbnail::imageCreateFromFile($input);
          else if ( is_string($input) ) return Thumbnail::imageCreateFromString($input);
          else return $input;
	}


	/**
	 * Create a GD image resource from file (JPEG, PNG support).
	 *
	 * @param  string $filename The image filename.
	 *
	 * @return mixed			GD image resource on success, FALSE on failure.
	 * @access public
	 * @static
	 */
	function imageCreateFromFile($filename)
	{
		if ( !is_file($filename) || !is_readable($filename) ) {
                     user_error(_C_JOIGALLERY_MSG_UPLOAD_ERR_FILE_OPEN.'"'.$filename.'"', E_USER_NOTICE);
                     return false;
		}

		// determine image format
		list( , , $type) = getimagesize($filename);

		switch ($type) {
		case IMAGETYPE_JPEG:
			return imagecreatefromjpeg($filename);
			break;
		case IMAGETYPE_PNG:
			return imagecreatefrompng($filename);
			break;
                case IMAGETYPE_GIF:
			return imagecreatefromgif($filename);
			break;
                case IMAGETYPE_BMP:
			return imagecreatefromwbmp($filename);
			break;
		}
		user_error(_C_JOIGALLERY_MSG_UPLOAD_ERR_IMG_TYPE, E_USER_NOTICE);
		return false;
	}

	// }}}
	// {{{

	/**
	 * Create a GD image resource from a string data.
	 *
	 * @param  string $string The string image data.
	 *
	 * @return mixed		  GD image resource on success, FALSE on failure.
	 * @access public
	 * @static
	 */
	function imageCreateFromString($string)	{
          
          if ( !is_string($string) || empty($string) ) {
               user_error(_C_JOIGALLERY_MSG_UPLOAD_ERR_IMG_VALUE_STRING, E_USER_NOTICE);
               return false;
          }

          return imagecreatefromstring($string);
	}

	// }}}
	// {{{

	/**
	 * Display rendered image (send it to browser or to file).
	 * This method is a common implementation to render and output an image.
	 * The method calls the render() method automatically and outputs the
	 * image to the browser or to the file.
	 *
	 * @param  mixed   $input   Destination image, a filename or an image string data or a GD image resource
	 * @param  array   $options Thumbnail options
	 *		 <pre>
	 *		 width   int	Width of thumbnail
	 *		 height  int	Height of thumbnail
	 *		 percent number Size of thumbnail per size of original image
	 *		 method  int	Method of thumbnail creating
	 *		 halign  int	Horizontal align
	 *		 valign  int	Vertical align
	 *		 </pre>
	 *
	 * @return boolean		  TRUE on success or FALSE on failure.
	 * @access public
	 */
	function output($input, $output=null, $options=array())	{

		// Load source file and render image
		$renderImage = Thumbnail::render($input, $options);
		if ( !$renderImage ) {
                     user_error(_C_JOIGALLERY_MSG_UPLOAD_ERR_IMG_RENDERING, E_USER_NOTICE);
                     return false;
		}

		// Set output image type
		// By default PNG image
		$type = isset($options['type']) ? $options['type'] : IMAGETYPE_PNG;

		// Before output to browsers send appropriate headers
		if (empty($output)) {
                    $content_type = image_type_to_mime_type($type);
                    if ( !headers_sent() ) {
                          header('Content-Type: ' . $content_type);
                    }
                    else {
                          user_error(_C_JOIGALLERY_MSG_UPLOAD_ERR_HEADERS_SENT, E_USER_NOTICE);
                          return false;
                    }
		}

		/* $quality = (int) $options['quality']; */ /* $quality = 20; */
		
		// Define outputing function
		switch ($type) {
		case IMAGETYPE_PNG:
			$result = empty($output) ? imagepng($renderImage) : imagepng($renderImage, $output);
			break;
		case IMAGETYPE_JPEG:
			$result = empty($output) ? imagejpeg($renderImage) : imagejpeg($renderImage, $output/* , $quality */);
			break;
                case IMAGETYPE_GIF:
			$result = empty($output) ? imagegif($renderImage) : imagegif($renderImage, $output);
			break;
                case IMAGETYPE_BMP:
			$result = empty($output) ? imagewbmp($renderImage) : imagewbmp($renderImage, $output);
			break;
		default:
			user_error( sprintf(_C_JOIGALLERY_MSG_UPLOAD_ERR_IMG_TYPE_PHP, $content_type), E_USER_NOTICE);
			return false;
		}

		// Output image (to browser or to file)
		if (!$result) {
                    user_error(_C_JOIGALLERY_MSG_UPLOAD_ERR_IMG_OUTPUT, E_USER_NOTICE);
                    return false;
		}

		// Free a memory from the target image
		imagedestroy($renderImage);

		return true;
	}

	
	/**
	 * Draw thumbnail result to resource.
	 *
	 * @param  mixed   $input   Destination image, a filename or an image string data or a GD image resource
	 * @param  array   $options Thumbnail options
	 *
	 * @return boolean TRUE on success or FALSE on failure.
	 * @access public
	 * @see	Thumbnail::output()
	 */
	function render($input, $options=array())
	{
		// Create the source image
		$sourceImage = Thumbnail::imageCreate($input);
		if ( ! is_resource($sourceImage) ) {
			user_error(_C_JOIGALLERY_MSG_UPLOAD_ERR_IMG_RESOURCE, E_USER_NOTICE);
			return false;
		}
		$sourceWidth  = imagesx($sourceImage);
		$sourceHeight = imagesy($sourceImage);

		// Set default options
		static $defOptions = array(
			'width'   => 150,
			'height'  => 150,
			'method'  => THUMBNAIL_METHOD_SCALE_MAX,
			'percent' => 0,
			'halign'  => THUMBNAIL_ALIGN_CENTER,
			'valign'  => THUMBNAIL_ALIGN_CENTER,
		);
		foreach ($defOptions as $k => $v) {
			if ( ! isset($options[$k]) ) {
				$options[$k] = $v;
			}
		}

		// Estimate a rectangular portion of the source image and a size of the target image
		if ( $options['method'] == THUMBNAIL_METHOD_CROP ) {
			if ( $options['percent'] ) {
				$W = floor($options['percent'] * $sourceWidth);
				$H = floor($options['percent'] * $sourceHeight);
			} else {
				$W = $options['width'];
				$H = $options['height'];
			}

			$width  = $W;
			$height = $H;

			$Y = Thumbnail::_coord($options['valign'], $sourceHeight, $H);
			$X = Thumbnail::_coord($options['halign'], $sourceWidth,  $W);
		} else {
			$X = 0;
			$Y = 0;

			$W = $sourceWidth;
			$H = $sourceHeight;

			if ( $options['percent'] ) {
				$width  = floor($options['percent'] * $W);
				$height = floor($options['percent'] * $H);
			} else {
				$width  = $options['width'];
				$height = $options['height'];

				if ( $options['method'] == THUMBNAIL_METHOD_SCALE_MIN ) {
					$Ww = $W / $width;
					$Hh = $H / $height;
					if ( $Ww > $Hh ) {
						$W = floor($width * $Hh);
						$X = Thumbnail::_coord($options['halign'], $sourceWidth,  $W);
					} else {
						$H = floor($height * $Ww);
						$Y = Thumbnail::_coord($options['valign'], $sourceHeight, $H);
					}
				} else {
					if ( $H > $W ) {
						$width  = floor($height / $H * $W);
					} else {
						$height = floor($width / $W * $H);
					}
				}
			}
		}

		// Create the target image
		if ( function_exists('imagecreatetruecolor') ) {
			$targetImage = imagecreatetruecolor($width, $height);
		} else {
			$targetImage = imagecreate($width, $height);
		}
		if ( ! is_resource($targetImage) ) {
			user_error(_C_JOIGALLERY_MSG_UPLOAD_ERR_GD_NEW, E_USER_NOTICE);
			return false;
		}

		// Copy the source image to the target image
		if ( $options['method'] == THUMBNAIL_METHOD_CROP ) {
			$result = imagecopy($targetImage, $sourceImage, 0, 0, $X, $Y, $W, $H);
		} elseif ( function_exists('imagecopyresampled') ) {
			$result = imagecopyresampled($targetImage, $sourceImage, 0, 0, $X, $Y, $width, $height, $W, $H);
		} else {
			$result = imagecopyresized($targetImage, $sourceImage, 0, 0, $X, $Y, $width, $height, $W, $H);
		}
		if ( ! $result ) {
			user_error(_C_JOIGALLERY_MSG_UPLOAD_ERR_IMG_RESIZE, E_USER_NOTICE);
			return false;
		}

		// Free a memory from the source image
		imagedestroy($sourceImage);

		// Save the resulting thumbnail
		return $targetImage;
	}

	// }}}
	// {{{ _coord()

	function _coord($align, $param, $src){
		if ( $align < THUMBNAIL_ALIGN_CENTER ) {
			$result = 0;
		} elseif ( $align > THUMBNAIL_ALIGN_CENTER ) {
			$result = $param - $src;
		} else {
			$result = ($param - $src) >> 1;
		}
		return $result;
	}

}

}